package com.ybear.ybcomponent.base.adapter;

import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.view.View;
import android.view.ViewParent;
import android.widget.PopupMenu;
import android.widget.TextView;

import androidx.annotation.DrawableRes;
import androidx.annotation.MenuRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.recyclerview.widget.RecyclerView;

import com.ybear.ybcomponent.OnTouchListener;
import com.ybear.ybcomponent.R;
import com.ybear.ybcomponent.TextHighlight;
import com.ybear.ybcomponent.Utils;
import com.ybear.ybcomponent.base.adapter.listener.OnItemClickListener;
import com.ybear.ybcomponent.base.adapter.listener.OnItemDownListener;
import com.ybear.ybcomponent.base.adapter.listener.OnItemLongClickListener;
import com.ybear.ybcomponent.base.adapter.listener.OnItemMenuClickListener;
import com.ybear.ybcomponent.highlight.ITextHighlight;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;

/**
 RecyclerView基本适配器
 */
public abstract class BaseRecyclerViewAdapter<E extends IItemData, H extends BaseViewHolder> extends
        RecyclerView.Adapter<H> implements IItemTouchStyle, IItemMenu<E>, IItemHolder<H>,
        IAdapterDao<E>, OnItemClickListener<E, H>, OnItemLongClickListener<E, H>, ITextHighlight {

    private final RecyclerView.Adapter<H> mAdapter;
    @NonNull
    private final List<E> mDataList;            //数据源
    @NonNull
    private final List<H> mHolderList;          //Holder
    private boolean isEnableTouchStyle = true;             //默认启用触摸样式
    private boolean isEnableTouchScaleAnimation = false;    //默认禁用触摸样式
    private boolean isEnablePopUpMenu = true;    //默认启用，调用menu点击事件时会弹出菜单
    @MenuRes
    private int menuRes = 0;
    private float mOnTouchAnimationByScaleValue = 0.98F;

    private PopupMenu mPopupMenu;
    private OnTouchListener mOnItemTouchListener;
    private OnItemDownListener<E, H> mOnItemDownListener;
    private OnItemClickListener<E, H> mOnItemClickListener;
    private OnItemLongClickListener<E, H> mOnItemLongClickListener;
    private OnItemMenuClickListener<E, H> mOnItemMenuClickListener;
    private OnItemMenuClickListener<E, H> mOnItemMenuLongClickListener;

    private final TextHighlight mTextHighlight = new TextHighlight();

    public BaseRecyclerViewAdapter(@NonNull List<E> list) {
        mDataList = list;
        mHolderList = new ArrayList<>();
        mAdapter = this;
    }

    public BaseRecyclerViewAdapter() { this( new ArrayList<>() ); }

    /**
     获取当前适配器
     @return    this
     */
    public RecyclerView.Adapter<H> getAdapter() { return mAdapter; }

    /**
     设置子项按下事件监听器
     @param l           监听器
     */
    public BaseRecyclerViewAdapter<E, H> setOnDownListener(OnItemDownListener<E, H> l) {
        mOnItemDownListener = l;
        return this;
    }

    /**
     子项按下事件监听器
     @return            监听器
     */
    public OnItemDownListener<E, H> getOnItemDownListener() { return mOnItemDownListener; }

    /**
     设置子项点击事件监听器
     @param l           监听器
     */
    public BaseRecyclerViewAdapter<E, H> setOnItemClickListener(OnItemClickListener<E, H> l) {
        mOnItemClickListener = l;
        return this;
    }

    /**
     子项点击事件监听器
     @return            监听器
     */
    public OnItemClickListener<E, H> getOnItemClickListener() { return mOnItemClickListener; }

    /**
     设置子项长按事件监听器
     @param l           监听器
     */
    public BaseRecyclerViewAdapter<E, H> setOnItemLongClickListener(OnItemLongClickListener<E, H> l) {
        mOnItemLongClickListener = l;
        return this;
    }

    /**
     子项长按事件监听器
     @return            监听器
     */
    public OnItemLongClickListener<E, H> getOnItemLongClickListener() {
        return mOnItemLongClickListener;
    }

    /**
     设置子项Touch事件监听器
     @param l           监听器
     */
    public BaseRecyclerViewAdapter<E, H>  setOnItemTouchListener(OnTouchListener l) {
        mOnItemTouchListener = l;
        return this;
    }

    /**
     子项Touch事件监听器
     @return            监听器
     */
    public OnTouchListener getOnItemTouchListener() { return mOnItemTouchListener; }

    /**
     子项带菜单点击事件监听器
     @param l     监听器
     */
    public BaseRecyclerViewAdapter<E, H>  setOnItemMenuClickListener(
            OnItemMenuClickListener<E, H> l, @MenuRes int menuRes) {
        mOnItemMenuClickListener = l;
        this.menuRes = menuRes;
        return this;
    }
    /**
     获取子项带菜单点击事件监听器
     @return      监听器
     */
    public OnItemMenuClickListener<E, H> getOnItemMenuClickListener() {
        return mOnItemMenuClickListener;
    }

    /**
     子项带菜单长按事件监听器
     @param l     监听器
     */
    public BaseRecyclerViewAdapter<E, H>  setOnItemMenuLongClickListener(
            OnItemMenuClickListener<E, H> l, @MenuRes int res) {
        mOnItemMenuLongClickListener = l;
        menuRes = res;
        return this;
    }

    /**
     获取子项带菜单长按事件监听器
     @return      监听器
     */
    public OnItemMenuClickListener<E, H> getOnItemMenuLongClickListener() {
        return mOnItemMenuLongClickListener;
    }

    /**
     是否启用/禁用点击菜单
     @param enable    是否启用
     */
    public BaseRecyclerViewAdapter<E, H>  setEnablePopUpMenu(boolean enable) {
        isEnablePopUpMenu = enable;
        return this;
    }

    /**
     是否启用/禁用点击的样式
     @param isEnable  是否启用
     */
    public BaseRecyclerViewAdapter<E, H>  setEnableTouchStyle(boolean isEnable) {
        isEnableTouchStyle = isEnable;
        return this;
    }

    /**
     是否启用/禁用点击的缩放样式
     @param isEnable  是否启用
     */
    public BaseRecyclerViewAdapter<E, H> setEnableTouchScaleAnimation(boolean isEnable) {
        return setEnableTouchScaleAnimation( isEnable, mOnTouchAnimationByScaleValue );
    }

    /**
     是否启用/禁用点击的缩放样式
     @param isEnable  是否启用
     */
    public BaseRecyclerViewAdapter<E, H> setEnableTouchScaleAnimation(boolean isEnable, float scale) {
        isEnableTouchScaleAnimation = isEnable;
        return setOnTouchAnimationByScaleValue( scale );
    }

    /**
     设置点击缩放样式的幅度
     @param scale  缩放幅度
     */
    public BaseRecyclerViewAdapter<E, H> setOnTouchAnimationByScaleValue(float scale) {
        mOnTouchAnimationByScaleValue = scale;
        return this;
    }

    /**
     绑定ViewHolder
     @param holder        Holder
     @param position      当前Item的位置
     */
    @Override
    public void onBindViewHolder(@NonNull H holder, int position) {
        //设置Item点击事件监听器
        holder.setOnClickListener(v -> doClick( v, true ));
        //设置Item长按事件监听器
        holder.setOnLongClickListener(v -> doClick( v, false ));
        //Item Touch事件监听器
        holder.setOnSuperTouchListener((v, ev) -> {
            //是否启用触摸的缩放效果
            if( isEnableTouchScaleAnimation ) {
                Utils.onTouchScaleAnimation( v, ev, mOnTouchAnimationByScaleValue );
            }
            //分发Touch事件
            if( mOnItemTouchListener != null ) return mOnItemTouchListener.onTouch( v, ev );
            return false;
        });

        //代替主View实现点击样式的view。同一个资源会return，无需担心会重复设置
        if( isEnableTouchStyle ) {
            holder.getTouchStyleView().setBackgroundResource( onTouchStyle() );
        }
        //加入到Holder列表
        addHolder( holder );
    }

    /**
     Holder发生改变时。即：获取，复用，回收等操作
     @param holder            改变的Holder
     @param position          位置 {@link H#getLayoutPosition()}
     @param holderStatus      获取时：{@link HolderStatus#ATTACHED}
                              复用时：{@link HolderStatus#RECYCLED}
                              回收时：{@link HolderStatus#DETACHED}
     */
    @Override
    public void onHolderChange(@NonNull H holder, int position, @HolderStatus int holderStatus) {}

    /**
     Holder进入到显示列表，等待被显示
     @param holder    等待显示的Holder
     */
    @Override
    public void onViewAttachedToWindow(@NonNull H holder) {
        super.onViewAttachedToWindow( holder );
        //添加Holder到列表
        addHolder( holder );
    }

    /**
     Holder进入到回收池（这个Holder已经被回收）
     @param h         被回收的Holder
     */
    @Override
    public void onViewRecycled(@NonNull H h) {
        super.onViewRecycled( h );
        //移除Item点击事件监听器
        h.setOnClickListener( null );
        //移除Item长按事件监听器
        h.setOnLongClickListener( null );
        //通知Holder发生改变
        onHolderChange( h, h.getLayoutPosition(), HolderStatus.RECYCLED );
    }

    /**
     Holder离开显示列表
     @param h         离开的Holder
     */
    @Override
    public void onViewDetachedFromWindow(@NonNull H h) {
        super.onViewDetachedFromWindow( h );
        //从列表中移除这个Holder
        if( !mHolderList.remove( h ) ) return;
        //通知Holder发生改变
        onHolderChange( h, h.getLayoutPosition(), HolderStatus.DETACHED );
    }

    /**
     添加Holder到Holder列表
     @param h         Holder
     */
    private void addHolder(@NonNull H h) {
        int index = mHolderList.indexOf( h );
        if( index != -1 ) {
            //列表存在这个Holder。如果列表的父容器为null，则替换列表中的Holder
            ViewParent parent = mHolderList.get( index ).getItemView().getParent();
            if( parent == null ) mHolderList.set( index, h );
        }else {
            //列表不存在这个Holder。添加传入的Holder
            if( !mHolderList.add( h ) ) return;
        }
        //通知Holder发生改变
        onHolderChange( h, h.getLayoutPosition(), HolderStatus.ATTACHED );
    }

    /**
     获取Holder
     @param position  Holder的位置
     @return          Holder。找不到返回：null
     */
    @Nullable
    public H getHolder(int position) {
        for( H h : mHolderList ) { if( h.getLayoutPosition() == position ) return h; }
        return null;
    }

    /**
     获取Holder
     @param v         Holder的父布局
     @return          Holder。找不到返回：null
     */
    @Nullable
    public H getHolder(@Nullable View v) {
        for( H h : mHolderList ) { if( h.getItemView().equals( v ) ) return h; }
        return null;
    }

    /**
     获取Holder的数量
     @return            数量
     */
    public int getHolderCount() { return mHolderList.size(); }

    /**
     获取全部Holder
     @return            全部Holder
     */
    public List<H> getHolderList() { return new ArrayList<>( mHolderList ); }

    /**
     处理点击事件
     @param v             点击的View
     @param isClick       是否为点击。true：点击，false：长按
     @return              长按返回的结果
     */
    private boolean doClick(View v, boolean isClick) {
        boolean isCall = false;
        H h = getHolder( v );
        if( h == null ) return false;
        int position = h.getLayoutPosition();
        E itemData = getItemData( position );
        //事件监听器
        if( isClick ) {
            //点击事件监听器
            onItemClick( this, v, itemData, position );
        }else {
            //长按事件监听器
            isCall = onItemLongClick( this, v, itemData, position );
        }
        //按下事件监听器
        if( mOnItemDownListener != null ) {
            mOnItemDownListener.onItemDown( this, v, itemData, position );
        }
        //设置显示菜单
        setShowMenu( v, itemData, position, isClick );
        return isCall;
    }

    /**
     显示菜单
     @param view      需要在指定view上显示
     */
    private void setShowMenu(View view, E itemData, int position, boolean isClick) {
        if( !isEnablePopUpMenu || menuRes == 0 ) return;
        if( isClick && mOnItemMenuClickListener == null ) return;
        if( !isClick && mOnItemMenuLongClickListener == null ) return;
        //获取弹出菜单
        PopupMenu menu = getPopupMenu( view );
        menu.getMenuInflater().inflate(menuRes, menu.getMenu());
        //设置属性
        callMenuAttr( menu, itemData );
        //菜单点击事件监听器
        menu.setOnMenuItemClickListener(menuItem -> {
            //单击
            if( isClick ) {
                if( mOnItemMenuClickListener == null ) return false;
                return mOnItemMenuClickListener.onItemMenuClick(
                        this, view, menuItem, itemData, position
                );
            }
            //长按
            if( mOnItemMenuLongClickListener == null ) return false;
            return mOnItemMenuLongClickListener.onItemMenuClick(
                    this, view, menuItem, itemData, position
            );
        });
        menu.show();
    }

    /**
     Item点击事件
     @param adapter   this
     @param v         点击的View
     @param data      Item数据
     @param position  位置
     */
    @Override
    public void onItemClick(RecyclerView.Adapter<H> adapter, View v, E data, int position) {
        if( mOnItemClickListener == null ) return;
        mOnItemClickListener.onItemClick( this, v, data, position );
    }

    /**
     Item长按事件
     @param v         长按的View
     @param data      Item数据
     @param position  位置
     */
    @Override
    public boolean onItemLongClick(RecyclerView.Adapter<H> adapter, View v, E data, int position) {
        return mOnItemLongClickListener != null &&
                mOnItemLongClickListener.onItemLongClick( this, v, data, position );
    }

    /**
     设置菜单属性
     @param menu          菜单
     @param itemData      item数据
     */
    @Override
    public void callMenuAttr(PopupMenu menu, E itemData) { /* 默认不做任何操作 */ }

    /**
     获取弹出菜单
     @param v                 需要弹出菜单的View
     @param isMultipleExist   允许多个菜单存在
     @return                  菜单
     */
    public PopupMenu getPopupMenu(View v, boolean isMultipleExist) {
        if( !isMultipleExist && mPopupMenu != null ) {
            mPopupMenu.dismiss();
            mPopupMenu.getMenu().clear();
            mPopupMenu.getMenu().close();
        }
        mPopupMenu = new PopupMenu(v.getContext(), v);
        return mPopupMenu;
    }

    /**
     获取弹出菜单
     @param v     需要弹出菜单的View
     @return      菜单
     */
    public PopupMenu getPopupMenu(View v) { return getPopupMenu(v, false); }

    /**
     数据源数量
     @return  数量
     */
    @Override
    public int getItemCount() { return mDataList.size(); }

    /**
     获取数据源中指定Item的HashCode
     @param position  位置
     @return          HashCode
     */
    @Override
    public int getItemDataHashCode(int position) { return mDataList.get( position ).hashCode(); }

    /**
     获取数据源的HashCode
     @return  HashCode
     */
    @Override
    public int getDataListHashCode() { return mDataList.hashCode(); }

    /**
     是否为空数据
     @return  结果
     */
    @Override
    public boolean isEmpty() { return mDataList.isEmpty(); }

    /**
     数据源排序
     @param c     排序规则
     */
    @RequiresApi(api = Build.VERSION_CODES.N)
    @Override
    public void sort(Comparator<? super E> c) {
        try {
            if( c != null ) mDataList.sort( c );
        }catch(IllegalArgumentException e) {
            e.printStackTrace();
        }
    }

    /**
     获取区间数据
     @param fromPotion    开始位置
     @param toPotion      结束位置
     @return              区间数据
     */
    @Override
    public List<E> subList(int fromPotion, int toPotion) {
        try {
            return mDataList.subList( fromPotion, toPotion );
        }catch(IndexOutOfBoundsException e) {
            e.printStackTrace();
        }
       return null;
    }

    /**
     传入的数据是否包含在数据源中
     @param c     数据
     @return      结果
     */
    @Override
    public boolean containsAll(Collection<E> c) { return c != null && mDataList.containsAll( c ); }

    /**
     获取数据源
     @return      数据源
     */
    @NonNull
    @Override
    public List<E> getDataList() { return mDataList; }

    /**
     获取当前Item数据（选中时）
     @return      ItemData
     */
    @Nullable
    @Override
    public E getItemData(int position) {
        if( mDataList.size() == 0 || !checkPosition( position ) ) return null;
        return mDataList.get( position );
    }

    /**
     获取第一个Item数据
     @return      ItemData
     */
    @Nullable
    @Override
    public E getItemDataOfFirst() { return getItemData( 0 ); }

    /**
     获取最后一个Item数据
     @return      ItemData
     */
    @Nullable
    @Override
    public E getItemDataOfLast() { return getItemData( getItemCount() - 1 ); }

    /**
     获取当前Item下标
     @param data  获取下标的数据源
     @return      下标
     */
    public int getItemDataPosition(E data) {
        for (int i = 0; i < mDataList.size(); i++) {
            E getData = mDataList.get( i );
            if( getData != null && getData.equals( data ) ) return i;
        }
        return -1;
    }

    /**
     增加一条数据
     @param data      数据
     @return          结果
     */
    @Override
    public boolean addItemData(E data) {
        try {
            return mDataList.add( data );
        }catch(IllegalArgumentException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     增加一条数据
     @param data      数据
     @return          结果
     */
    @Override
    public boolean addItemData(List<E> data) {
        try {
            return mDataList.addAll( data );
        }catch(IllegalArgumentException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     增加一条数据
     @param position  位置
     @param data      数据
     @return          结果
     */
    @Override
    public boolean addItemData(int position, E data) {
        if( !checkPosition( position ) ) return false;
        mDataList.add( position, data );
        return true;
    }

    /**
     增加一条数据
     @param position  位置
     @param data      数据
     @return          结果
     */
    @Override
    public boolean addItemData(int position, List<E> data) {
        return checkPosition( position ) && mDataList.addAll( position, data );
    }

    /**
     覆盖原有的数
     @param data        数据源
     */
    @Override
    public void setItemData(List<E> data) {
        clearItemData();
        addItemData( data );
    }

    /**
     更新一条数据
     @param position  位置
     @param data      数据源
     @return          之前的数据
     */
    @Override
    public E setItemData(int position, E data) {
        return checkPosition( position ) ? mDataList.set( position, data ) : null;
    }

    /**
     移除一条数据
     @param position  位置
     @return          之前的数据
     */
    @Override
    public E removeItemData(int position) {
        return checkPosition( position ) ? mDataList.remove( position ) : null;
    }

    /**
     移除一条数据
     @param data      删除的数据
     @return          之前的数据
     */
    @Override
    public E removeItemData(E data) {
        try {
            return mDataList.remove( data ) ? data : null;
        }catch(UnsupportedOperationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     清空数据源
     */
    @Override
    public void clearItemData() { mDataList.clear(); }

    /**
     获取{@link Resources}
     @param h         Holder
     @return          {@link Resources}
     */
    public Resources getResources(@NonNull H h) {
        Context context = getContext( h );
        return context != null ? context.getResources() : null;
    }

    /**
     获取上下文
     @param h         Holder
     @return          {@link Context}
     */
    public Context getContext(@NonNull H h) { return h.getContext(); }

    /**
     按下时的样式
     @return  资源
     */
    @DrawableRes
    @Override
    public int onTouchStyle() { return R.drawable.selector_def_item_btn; }

    /**
     下标检查
     @param position  下标
     @return          是否安全
     */
    public boolean checkPosition(int position) { return position >= 0 && position < mDataList.size(); }

    /**
     * 设置高亮文本
     * @param tv                设置高亮文本的TextView
     * @param text              文本内容
     * @param highlight         高亮文本
     * @param color             高亮文本色
     * @param highlightCount    重复文本的高亮次数。-1：无限制
     */
    @Override
    public void setTextAndHighlight(TextView tv, String text, String highlight, int color, int highlightCount) {
        mTextHighlight.setTextAndHighlight( tv, text, highlight, color, highlightCount );
    }

    /**
     * 设置高亮文本
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     * @param color         高亮文本色
     */
    @Override
    public void setTextAndHighlight(TextView tv, String text, String highlight, int color) {
        mTextHighlight.setTextAndHighlight( tv, text, highlight, color );
    }

    /**
     * 设置高亮文本
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     */
    @Override
    public void setTextAndHighlight(TextView tv, String text, String highlight) {
        mTextHighlight.setTextAndHighlight( tv, text, highlight );
    }

    /**
     * 设置高亮文本背景
     * @param tv                设置高亮文本的TextView
     * @param text              文本内容
     * @param highlight         高亮文本
     * @param color             高亮文本色
     * @param highlightCount    重复文本的高亮次数。-1：无限制
     */
    @Override
    public void setTextAndHighlightByBackground(TextView tv, String text, String highlight,
                                                int color, int highlightCount) {
        mTextHighlight.setTextAndHighlightByBackground( tv, text, highlight, color, highlightCount );
    }

    /**
     * 设置高亮文本背景
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     * @param color         高亮文本色
     */
    @Override
    public void setTextAndHighlightByBackground(TextView tv, String text, String highlight, int color) {
        mTextHighlight.setTextAndHighlightByBackground( tv, text, highlight, color );
    }

    /**
     * 设置高亮文本背景
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     */
    @Override
    public void setTextAndHighlightByBackground(TextView tv, String text, String highlight) {
        mTextHighlight.setTextAndHighlightByBackground( tv, text, highlight );
    }

    /**
     * 设置高亮文本和背景文本
     * @param tv                设置高亮文本的TextView
     * @param text              文本内容
     * @param highlight         高亮文本
     * @param foreColor         高亮文本色
     * @param backColor         高亮背景色
     * @param highlightCount    重复文本的高亮次数。-1：无限制
     */
    @Override
    public void setTextAndHighlightByForeAndBack(TextView tv, String text, String highlight,
                                                 int foreColor, int backColor, int highlightCount) {
        mTextHighlight.setTextAndHighlightByForeAndBack(
                tv, text, highlight, foreColor, backColor, highlightCount
        );
    }

    /**
     * 设置高亮文本和背景文本
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     * @param foreColor         高亮文本色
     * @param backColor         高亮背景色
     */
    @Override
    public void setTextAndHighlightByForeAndBack(TextView tv, String text, String highlight,
                                                 int foreColor, int backColor) {
        mTextHighlight.setTextAndHighlightByForeAndBack( tv, text, highlight, foreColor, backColor );
    }

    /**
     * 设置高亮文本和背景文本
     * @param tv            设置高亮文本的TextView
     * @param text          文本内容
     * @param highlight     高亮文本
     */
    @Override
    public void setTextAndHighlightByForeAndBack(TextView tv, String text, String highlight) {
        mTextHighlight.setTextAndHighlightByForeAndBack( tv, text, highlight );
    }
}